package com.atguigu.gmall.gateway.filter;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.IpUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;

@Component
public class AuthGlobalFilter implements GlobalFilter {

    @Value("${authUrls.url}")
    private String authUrls; // authUrls = trade.html,myOrder.html,list.html

    @Autowired
    private RedisTemplate redisTemplate;
    //  声明一个类.
    private AntPathMatcher antPathMatcher = new AntPathMatcher();
    /**
     *
     * @param exchange 服务器请求对象.
     * @param chain 过滤器链;
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //  获取到用户的请求:
        ServerHttpRequest request = exchange.getRequest();
        //  获取响应：
        ServerHttpResponse response = exchange.getResponse();
        //  获取请求路径：http://localhost/api/product/inner/getSkuInfo/22
        //  RequestPath path = request.getPath();
        //  request.getURI() http://localhost/api/product/inner/getSkuInfo/22
        String path = request.getURI().getPath(); // /api/product/inner/getSkuInfo/22
        //  内部数据接口/inner/xxx
        if (antPathMatcher.match("/api/*/inner/**",path)){
            //  做响应；不能访问！
            return out(response, ResultCodeEnum.ILLEGAL_REQUEST);
        }
        //  获取用户Id
        String userId = this.getUserId(request);
        //  获取临时用户Id
        String userTempId = this.getUserTempId(request);
        if ("-1".equals(userId)){
            return out(response, ResultCodeEnum.PERMISSION);
        }
        //  判断
        if (antPathMatcher.match("/api/**/auth/**",path)){
            //  判断这个用户Id 是空.
            if (StringUtils.isEmpty(userId)){
                //  做响应；不能访问！
                return out(response, ResultCodeEnum.LOGIN_AUTH);
            }
        }

        //  判断当前这个用户访问的业务层控制器是否需要登录. myOrder.html;
        /*
        authUrls:
            url: trade.html,myOrder.html,list.html
         */
        //  authUrls = trade.html,myOrder.html,list.html
        //  http://order.gmall.com/myOrder.html
        String[] split = authUrls.split(",");
        if (split!=null && split.length>0){
            //  循环遍历.
            for (String url : split) {
                //  能找到，则返回找到的下标，如果找不到返回 -1
                //  如果能找到，并且这个属于未登录状态，则需要跳转到登录页面。
                if (path.indexOf(url)!=-1 && StringUtils.isEmpty(userId)){
                    //  登录页面.
                    response.setStatusCode(HttpStatus.SEE_OTHER);
                    //  重定向的地址："http://passport.gmall.com/login.html?originUrl="+request.getURI();
                    //  http://passport.gmall.com/login.html?originUrl=http://list.gmall.com/list.html?category3Id=61
                    response.getHeaders().set(HttpHeaders.LOCATION,"http://passport.gmall.com/login.html?originUrl="+request.getURI());
                    //  重定向了.
                    return response.setComplete();
                }
            }
        }
        //  难一点的，就要面向对象. 工具.
        //  校验完成之后，将用户Id 存储到请求头
        if (!StringUtils.isEmpty(userId) || !StringUtils.isEmpty(userTempId)){
            //  判断登录用户Id
            if (!StringUtils.isEmpty(userId) ){
                //  如何添加 header。 ServerHttpRequest request
                //  将request封装header. ServerHttpRequest。
                request.mutate().header("userId",userId).build();
            }
            //  判断未登录用户Id
            if (!StringUtils.isEmpty(userTempId) ){
                //  如何添加 header。 ServerHttpRequest request
                //  将request封装header. ServerHttpRequest。
                request.mutate().header("userTempId",userTempId).build();
            }
            //  request 将带有请求头的对象 转换为 ---> exchange
            chain.filter(exchange.mutate().request(request).build());
        }
        //  默认过滤成功.
        return chain.filter(exchange);
    }

    /**
     * 获取临时用户Id
     * @param request
     * @return
     */
    private String getUserTempId(ServerHttpRequest request) {
        //  声明一个临时用户Id
        String userTempId = "";
        //  从cookie 中获取临时用户Id
        HttpCookie httpCookie = request.getCookies().getFirst("userTempId");
        if (httpCookie!=null){
            userTempId = httpCookie.getValue();
        } else {
            //  从请求头中获取数据.
            List<String> list = request.getHeaders().get("userTempId");
            if (!CollectionUtils.isEmpty(list)){
                userTempId = list.get(0);
            }
        }
        //  思考一个问题！登录之后，临时用户Id 存储在请求头中.
        //  返回临时用户Id
        return userTempId;
    }

    /**
     * 获取用户Id
     * @param request
     * @return
     */
    private String getUserId(ServerHttpRequest request) {
        //  组成缓存的key、 由token 组成. cookie ，header 中.
        String token = "";
        HttpCookie httpCookie = request.getCookies().getFirst("token");
        if (httpCookie!=null){
            //  获取token 数据.
            token = httpCookie.getValue();
        }else {
            //  从header 中获取！
            List<String> list = request.getHeaders().get("token");
            //            String token1 = request.getHeaders().getFirst("token");
            //            System.out.println(token1+":\t token1");
            if (!CollectionUtils.isEmpty(list)){
                token = list.get(0);
            }
        }
        //  判断token 是否为空.
        if (!StringUtils.isEmpty(token)){
            // 组成登录key
            String userLoginKey = "user:login:"+token;
            //  获取缓存数据..
            String userJson = (String) redisTemplate.opsForValue().get(userLoginKey);
            if (!StringUtils.isEmpty(userJson)){
                //  进行转换.
                JSONObject user = JSONObject.parseObject(userJson);
                String ip = (String) user.get("ip");
                if (ip.equals(IpUtil.getGatwayIpAddress(request))){
                    String userId = (String) user.get("userId");
                    return userId;
                } else {
                  return "-1";
                }
            }
        }
        return "";
    }

    private Mono<Void> out(ServerHttpResponse response, ResultCodeEnum resultCodeEnum) {
        //  信息提示：
        //  String message = resultCodeEnum.getMessage(); // 非法请求.
        //  ResultCodeEnum 封装到 Result 中
        Result<Object> result = Result.build(null, resultCodeEnum);
        //  result ---> 变为字符串.
        String str = JSONObject.toJSONString(result);
        //  找到输出方法.
        DataBuffer wrap = response.bufferFactory().wrap(str.getBytes());
        //  设置字符集
        response.getHeaders().set("Content-Type","application/json;charset=UTF-8");
        //  返回数据.
        return response.writeWith(Mono.just(wrap));
    }

}
